package org.six11.olive3.picturephone.server;

import java.util.List;

import javax.servlet.ServletException;

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import org.six11.olive3.server.DatabaseThing;
import org.six11.util2.Debug;

/**
 * 
 * 
 * @author Gabe Johnson <johnsogg@cmu.edu>
 */
public class Find {

  public static boolean bugEnabled = false;

  /**
   * @param args
   */
  public static void main(String[] args) throws ServletException {
    Debug.useColor = false;
    MysqlDataSource dbSource = new MysqlDataSource();
    dbSource.setServerName("localhost");
    dbSource.setDatabaseName("sketching_games");
    dbSource.setUser("olive");
    dbSource.setPassword("secret");
    DatabaseThing staticDb = new DatabaseThing(dbSource);
    Find find = new Find(staticDb);
    String getAllUsersSql = "select id from user";
    List<Integer> userIDs = staticDb.getIDs(getAllUsersSql, empty);
    for (int userID : userIDs) {
      find.findAllToDo("" + userID);
    }
  }

  private static String[] empty = new String[] {};
  private final static String TYPE_SKETCH = "sketch";
  private final static String TYPE_DESC = "description";
  private DatabaseThing db;
  private int numColsOutput = 0;

  public Find(DatabaseThing db) {
    this.db = db;
  }

  public void findAllToDo(String userID) throws ServletException {
    findAllSketchesToDo(userID);
    findAllDescriptionsToDo(userID);
    findAllRatingsToDo(userID);
  }

  private void findAllRatingsToDo(String userID) throws ServletException {
    String lastIDSql = "select last_todo_rate from user where id = ?";
    String lastID = "" + db.getInt(lastIDSql, userID);
    String getAllRatesSql = "select id from sketch where id > ?";
    String addTodo = "insert into todo_rate (user_id, sketch_id) values ( ?, ? )";
    String updateLastID = "update user set last_todo_rate = ? where id = ?";
    List<Integer> sketchIDs = db.getIDs(getAllRatesSql, lastID);
    for (int sketchID : sketchIDs) {
      boolean ok = mayRate("" + sketchID, userID);
      if (ok) {
        db.insertRow(addTodo, userID, "" + sketchID);
      }
      explain(ok, 'R');
      db.insertRow(updateLastID, "" + sketchID, userID);
    }
  }

  /**
   * This is just like the 'exclude' methods. It returns true if it is OK for the user to rate this
   * sketch.
   */
  public boolean mayRate(String sketchID, String userID) throws ServletException {
    return excludeSelf(sketchID, userID, TYPE_SKETCH) && //
        excludeSelfParent(sketchID, userID, TYPE_SKETCH) && //
        excludeSelfParentParent(sketchID, userID, TYPE_SKETCH) && //
        excludeSelfRated(sketchID, userID, TYPE_SKETCH);
  }

  /**
   * Tells you if the user may make a description based on the given sketch.
   */
  public boolean mayDescribe(String sketchID, String userID) throws ServletException {
    return exclude(sketchID, userID, TYPE_SKETCH);
  }

  /**
   * Tells you if the user may make a sketch based on the given description.
   */
  public boolean maySketch(String descID, String userID) throws ServletException {
    return exclude(descID, userID, TYPE_DESC);
  }

  private void findAllDescriptionsToDo(String userID) throws ServletException {
    String lastIDSql = "select last_todo_describe from user where id = ?";
    String lastID = "" + db.getInt(lastIDSql, userID);
    String getAllDescriptionsSql = "select id from description where id > ?";
    String addTodo = "insert into todo_describe (user_id, describe_id) values ( ?, ? )";
    String updateLastID = "update user set last_todo_describe = ? where id = ?";
    List<Integer> descIDs = db.getIDs(getAllDescriptionsSql, lastID);
    for (int descID : descIDs) {
      boolean ok = maySketch("" + descID, userID);
      if (ok) {
        db.insertRow(addTodo, userID, "" + descID);
      }
      explain(ok, 'D');
      db.insertRow(updateLastID, "" + descID, userID);
    }
  }

  private void findAllSketchesToDo(String userID) throws ServletException {
    String lastIDSql = "select last_todo_sketch from user where id = ?";
    String lastID = "" + db.getInt(lastIDSql, userID);
    String getAllSketchesSql = "select id from sketch where id > ?";
    String addTodo = "insert into todo_sketch (user_id, sketch_id) values ( ?, ? )";
    String updateLastID = "update user set last_todo_sketch = ? where id = ?";
    List<Integer> sketchIDs = db.getIDs(getAllSketchesSql, lastID);
    for (int sketchID : sketchIDs) {
      boolean ok = mayDescribe("" + sketchID, userID);
      if (ok) {
        db.insertRow(addTodo, userID, "" + sketchID);
      }
      explain(ok, 'S');
      db.insertRow(updateLastID, "" + sketchID, userID);
    }
  }

  private String getParent(String type) {
    if (type.equals(TYPE_SKETCH)) {
      return TYPE_DESC;
    } else {
      return TYPE_SKETCH;
    }
  }

  private void explain(boolean ok, char symbol) {
    if (numColsOutput > 80) {
      System.out.println();
      numColsOutput = 0;
    }
    if (ok) {
      System.out.print(symbol);
    } else {
      System.out.print("-");
    }
    numColsOutput += 1;
  }

  /**
   * This returns TRUE if the given object can be acted on right now.
   */
  private boolean exclude(String objectID, String userID, String type) throws ServletException {
    boolean ok = excludeSelf(objectID, userID, type) && //
        excludeSelfParent(objectID, userID, type) && //
        excludeSelfParentParent(objectID, userID, type) && //
        excludeSelfParentChildren(objectID, userID, type) && //
        excludeSelfChildren(objectID, userID, type) && //
        excludeSelfChildrenChildren(objectID, userID, type) && //
        excludeSelfRated(objectID, userID, type);
    return ok;
  }

  private boolean excludeSelf(String objectID, String userID, String type) throws ServletException {
    String sql = "select count(id) from " + type + " where id=? and user_id=?";
    int count = db.getInt(sql, objectID, userID);
    boolean ret = (count == 0);
    if (!ret) {
      bug ("excludeSelf: " + objectID + " " + userID + " " + type);
    }
    return ret;
  }

  private boolean excludeSelfParent(String objectID, String userID, String type)
      throws ServletException {
    String parent = getParent(type);
    String parentID = parent + "_id";
    String myID = type + "_id";
    String sql = "select count(X.id) from " + type + " as X join " + parent + " as Y on X."
        + parentID + " = Y.id where X.id = ? " + "and (X.user_id = ? or Y.user_id = ?)";
    int count = db.getInt(sql, objectID, userID);
    boolean ret = (count == 0);
    if (!ret) {
      bug ("excludeSelfParent: " + objectID + " " + userID + " " + type);
    }
    return ret;
  }

  private boolean excludeSelfParentParent(String objectID, String userID, String type)
      throws ServletException {
    String parent = getParent(type);
    String parentID = parent + "_id";
    String myID = type + "_id";
    String sql = "select count(X.id) from " + type + " as X join " + parent + " as Y join " + type
        + " as Z on X." + parentID + " = Y.id and Y." + myID + " = Z.id where X.id = ? "
        + "and (X.user_id = ? or Y.user_id = ? or Z.user_id = ?)";
    int count = db.getInt(sql, objectID, userID);
    boolean ret = (count == 0);
    if (!ret) {
      bug ("excludeSelfParentParent: " + objectID + " " + userID + " " + type);
    }
    return ret;
  }

  private boolean excludeSelfParentChildren(String objectID, String userID, String type)
      throws ServletException {
    String parent = getParent(type);
    String parentID = parent + "_id";
    String sql = "select count(X.id) from " + type + " as X join " + parent + " as Y join " + type
        + " as Z " + "on X." + parentID + " = Y.id and Z." + parentID + " = Y.id "
        + "where  X.id = ? and (X.user_id = ? or Y.user_id = ? or Z.user_id = ?)";
    int count = db.getInt(sql, objectID, userID);
    boolean ret = (count == 0);
    if (!ret) {
      bug ("excludeSelfParentChildren: " + objectID + " " + userID + " " + type);
    }
    return ret;
  }

  private boolean excludeSelfChildren(String objectID, String userID, String type)
      throws ServletException {
    String parent = getParent(type); // also the child type
    String parentID = parent + "_id"; // also the child id
    String sql = "select count(X.id) from " + type + " as X join " + parent + " as Y " + "on X."
        + parentID + " = Y.id where X.id = ? and (X.user_id = ? or Y.user_id = ?)";
    int count = db.getInt(sql, objectID, userID);
    boolean ret = (count == 0);
    if (!ret) {
      bug ("excludeSelfChildren: " + objectID + " " + userID + " " + type);
    }
    return ret;
  }

  private boolean excludeSelfChildrenChildren(String objectID, String userID, String type)
      throws ServletException {
    String parent = getParent(type); // also the child type
    String parentID = parent + "_id"; // also the child id
    String myID = type + "_id";
    String sql = "select count(X.id) from " + type + " as X join " + parent + " as Y join " + type
        + " as Z on X.id = Y." + myID + " and Y.id = Z." + parentID
        + " where X.id = ? and (X.user_id = ? or Y.user_id = ? or Z.user_id = ?)";
    int count = db.getInt(sql, objectID, userID);
    boolean ret = (count == 0);
    if (!ret) {
      bug ("excludeSelfChildrenChildren: " + objectID + " " + userID + " " + type);
    }
    return ret;
  }

  private boolean excludeSelfRated(String objectID, String userID, String type)
      throws ServletException {
    boolean ret = true;
    if (type.equals(TYPE_SKETCH)) {
      String sql = "select count(id) from judgement where sketch_id = ? and user_id = ?";
      int count = db.getInt(sql, objectID, userID);
      ret = (count == 0);
    }
    if (!ret) {
      bug ("excludeSelfRated: " + objectID + " " + userID + " " + type);
    }
    return ret;
  }

  private static void bug(String what) {
    if (bugEnabled) {
      Debug.out("Find", what);
    }
  }

  public void findAllToDoInBackground(final String uid) {
    Runnable r = new Runnable() {
      public void run() {
        try {
          findAllToDo(uid);
        } catch (ServletException ex) {
          ex.printStackTrace();
        }
      }
    };
    new Thread(r).start();
  }
}
